package com.time.workbench.efficient.handle;


import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.extra.spring.SpringUtil;
import com.jcraft.jsch.Session;
import com.time.workbench.efficient.config.GitConfig;
import com.time.workbench.efficient.consts.CmdConst;
import com.time.workbench.efficient.entity.dto.ServerSessionConnect;
import com.time.workbench.efficient.entity.dto.ServerSessionInfo;
import com.time.workbench.efficient.enums.DeployStepEnum;
import com.time.workbench.efficient.util.DeployUtil;
import com.time.workbench.efficient.util.JschUtil;
import com.time.workbench.release.consts.BuildConfig;
import lombok.AccessLevel;
import lombok.Data;
import lombok.Getter;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.RedisTemplate;

import java.io.File;
import java.nio.charset.StandardCharsets;
import java.time.Duration;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Consumer;

/**
 * 部署抽象类
 * 子类不做spring依赖控制,由上级调用做
 * 定义部署步奏 (中间步奏不需要 可以不重写)
 * 1: 连接服务器
 * 2: 拉取代码
 * 3: 打包
 * 4: 停止旧版本应用
 * 5: 备份老的包
 * 6: 移动包到应用目录下
 * 7: 启动
 * 8: 监听应用状态
 * 9: 释放资源
 * 10: 失败/成功通知
 */
@Data
@Slf4j
public abstract class AbstractDeploy implements DeployInterface {

    /**
     * 服务器链接信息集合
     */
    protected List<ServerSessionConnect> serverSessionConnects;

    /**
     * 部署任务id
     */
    private String deployTaskId;

    /**
     * 部署成功钩子
     */
    private Consumer<String> startUpSuccessConsumer;

    /**
     * 部署失败钩子
     */
    private Consumer<String> startUpFailConsumer;


    /**
     * 构建成功钩子
     */
    private Consumer<String> buildSuccessConsumer;

    /**
     * 部署失败钩子
     */
    private Consumer<String> buildFailConsumer;

    /**
     * jar包名称(内部维护)
     */
    @Setter
    @Getter
    protected String packageFileName;

    /**
     * 服务器连接对象(内部维护)
     */
    @Setter
    @Getter
    protected List<ServerSessionInfo> serverSessions = new ArrayList<>();

    /**
     * 项目路径 (内部维护)
     */
    @Setter
    @Getter
    protected String projectPath;

    protected BuildConfig buildConfig;

    /**
     * 服务器连接对象(内部维护)
     */
    @Setter(AccessLevel.NONE)
    protected Session buildServerSession;

    public AbstractDeploy() {
        buildConfig = SpringUtil.getBean(BuildConfig.class);
    }


    @Override
    public boolean build() {
        try {

            init();
            connectServer();
            pullCode();
            boolean isSuccess = packageCode();
            if (isSuccess) {
                log.info("构建成功");
                // 构建成功钩子
                if (buildSuccessConsumer != null)
                    buildSuccessConsumer.accept(deployTaskId);
            }
            // 构建失败钩子
            else if (buildFailConsumer != null) {
                log.info("构建失败");
                buildFailConsumer.accept(deployTaskId);
            }
            return isSuccess;

        } catch (Exception e) {
            log.info("构建失败", e);
            buildFailConsumer.accept(deployTaskId);
            return false;
        } finally {
            notice();
            freed(null);
        }
    }


    @Override
    public void startUp() {
        try {
            init();

            for (ServerSessionInfo serverSessionInfo : serverSessions) {
                try {
                    connectServer(serverSessionInfo);
                    stop(serverSessionInfo);
                    backupPackage(serverSessionInfo);
                    movePackage(serverSessionInfo);
                    start(serverSessionInfo);
                    if (monitorStatus(serverSessionInfo)) {
                        // 部署成功钩子
                        if (startUpSuccessConsumer != null) {
                            log.info("部署成功");
                            startUpSuccessConsumer.accept(deployTaskId);
                            return;
                        }
                    }
                    // 部署失败钩子
                    if (startUpFailConsumer != null) {
                        log.info("部署失败");
                        startUpFailConsumer.accept(deployTaskId);
                    }
                } finally {
                    freed(serverSessionInfo);
                }

            }


        } catch (Exception e) {
            notice();
            log.info("部署失败", e);
            startUpFailConsumer.accept(deployTaskId);
        } finally {
            notice();
        }
    }

    /**
     * 初始化数据
     */
    public void init() {
    }

    /**
     * 连接服务器
     */
    public void connectServer() {
    }

    /**
     * 连接服务器
     */
    public void connectServer(ServerSessionInfo serverSession) {
        DeployUtil.connectServer(serverSession.getServerSession());
    }

    /**
     * 拉取代码
     */
    public void pullCode() {
    }

    /**
     * 打包
     */
    public boolean packageCode() {
        return false;
    }

    /**
     * 停止应用
     */
    public void stop(ServerSessionInfo serverSessionInfo) {
    }

    /**
     * 备份老的包
     */
    public void backupPackage(ServerSessionInfo serverSessionInfo) {
    }

    /**
     * 移动包到应用目录下
     */
    public void movePackage(ServerSessionInfo serverSessionInfo) {
    }

    /**
     * 启动
     */
    public void start(ServerSessionInfo serverSessionInfo) {
    }

    /**
     * 监听应用状态
     *
     * @return 是否启动成功
     */
    public boolean monitorStatus(ServerSessionInfo serverSessionInfo) {
        return false;
    }

    /**
     * 释放资源
     */
    public void freed(ServerSessionInfo serverSessionInfo) {
    }

    /**
     * 通知
     */
    public void notice() {
    }

    protected void pullExtracted(List<ServerSessionInfo> serverSessions, Session buildServerSession, String sourceGitPullBaseDirectory, String sourceGitSourceDirect, BuildConfig buildConfig, String projectCode, String gitRemoteAddress, String branch) {
        if (buildConfig.getMode().equals("gather")) {
            String gitPullBaseDirectory = buildConfig.getGitPullBaseDirectory();
            String gitSourceDirect = gitPullBaseDirectory + File.separator + projectCode;
            List<String> cmdArray = DeployUtil.gitPullCodeCmd(gitPullBaseDirectory, projectCode, gitSourceDirect, gitRemoteAddress, branch);
            JschUtil.execByShell(buildServerSession, cmdArray, StandardCharsets.UTF_8);
            return;
        }
        for (ServerSessionInfo serverSession : serverSessions) {
            Session session = serverSession.getServerSession();
            List<String> cmdArray = DeployUtil.gitPullCodeCmd(sourceGitPullBaseDirectory, projectCode, sourceGitSourceDirect, gitRemoteAddress, branch);
            JschUtil.execByShell(session, cmdArray, StandardCharsets.UTF_8);
        }
    }

    protected String packageExtracted(List<ServerSessionInfo> serverSessions, Session buildServerSession, String sourceGitSourceDirect, String projectCode, String packageModule, String findLastName) {

        String gitSourceDirect = sourceGitSourceDirect;
        if (buildConfig.getMode().equals("gather")) {
            String gitPullBaseDirectory = buildConfig.getGitPullBaseDirectory();
            gitSourceDirect = gitPullBaseDirectory + File.separator + projectCode;
            // 如果是集中服务器
            packageFileName = DeployUtil.mavenPackage(gitSourceDirect, packageModule, buildServerSession, findLastName);
        } else {
            for (ServerSessionInfo serverSession : serverSessions) {
                Session session = serverSession.getServerSession();

                serverSession.setPackageFileName(DeployUtil.mavenPackage(sourceGitSourceDirect, packageModule, session, findLastName));
            }

        }


        if (buildConfig.getMode().equals("gather")) {


            for (ServerSessionInfo serverSession : serverSessions) {
                // 初始化原目录
                String source = gitSourceDirect + File.separator + packageModule + File.separator + "target" + File.separator + serverSession.getPackageFileName();
                String target = sourceGitSourceDirect + File.separator + packageModule + File.separator + "target";
                JschUtil.exec(serverSession.getServerSession(), StrUtil.format(CmdConst.mkdirP, target), StandardCharsets.UTF_8);
                List<String> cmdArray = new ArrayList<>();
                ServerSessionConnect serverSessionConnect = serverSession.getServerSessionConnect();
                cmdArray.add(StrUtil.format(CmdConst.scp, serverSessionConnect.getPort(), source, serverSessionConnect.getUserName(), serverSessionConnect.getIp(), target));
                JschUtil.srpShell(buildServerSession, cmdArray, serverSessionConnect.getPassword(), StandardCharsets.UTF_8);
            }
        }

        return packageFileName;
    }

    protected boolean isExistPackName() {
        return StrUtil.isNotEmpty(packageFileName) || serverSessions.stream().map(ServerSessionInfo::getPackageFileName).allMatch(StrUtil::isNotEmpty);
    }


    protected Session getSession(ServerSessionInfo serverSessionInfo) {
        return buildConfig.getMode().equals("gather") ? buildServerSession : serverSessionInfo.getServerSession();
    }
}
